

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


#include "ttusb_comn.h"
#include "ttusb.h"



/**/
int ttusb_desc_init( usbd_config_t * pcfg )
{
    memset( pcfg, 0, sizeof(usbd_config_t) );
    return 0;
}

/* config: device */
int ttusb_desc_device_vid_pid( usbd_config_t * pcfg, uint16_t vid, uint16_t pid )
{
    pcfg->vid = vid;
    pcfg->pid = pid;
    return 0;
}


int ttusb_desc_device_str_manu( usbd_config_t * pcfg, char * str )
{
    pcfg->str_manu = str;
    return 0;
}


int ttusb_desc_device_str_prod( usbd_config_t * pcfg, char * str )
{
    pcfg->str_prod = str;
    return 0;
}


int ttusb_desc_device_str_seri( usbd_config_t * pcfg, char * str )
{
    pcfg->str_seri = str;
    return 0;
}


/**/
int ttusb_desc_config_str( usbd_config_t * pcfg, char * str )
{
    pcfg->str_cnfg = str;
    return 0;
}


int ttusb_desc_config_power( usbd_config_t * pcfg, uint8_t pwr )
{
    pcfg->maxpower = pwr;
    return 0;
}


/* config: interface, endpoint */
int ttusb_desc_interface_str( usbd_config_t * pcfg, char * str )
{
    pcfg->iface[0].str_intf = str;
    return 0;
}


/*
OUT : addr
 IN : 0x80 | addr
*/
static int ttusb_desc_interface_add_endp( usbd_config_t * pcfg, uint8_t ios, bulk_proc_cbk cbkf )
{
    int  eidx;
    usbd_intf_t * pifc = &( pcfg->iface[0] );

    /**/
    if ( pcfg->used >= 15 )  {
        return 1;
    }

    if ( pifc->epcnt >= 4 )  {
        return 2;
    }

    /**/
    eidx = pcfg->used;
    pcfg->used += 1;
    pcfg->endps[eidx].pifc = pifc;
    pcfg->endps[eidx].pid = 0;
    pcfg->endps[eidx].pfunc = cbkf;

    /**/
    pifc->epadrs[pifc->epcnt] = (eidx + 1) | ios;
    pifc->epcnt += 1;
    return 0;
}


int ttusb_desc_interface_add_inp( usbd_config_t * pcfg, bulk_proc_cbk cbkf )
{
    int iret;

    iret = ttusb_desc_interface_add_endp( pcfg, USB_DIR_IN, cbkf );
    if ( 0 != iret )  {
        return iret;
    }

    return 0;
}


int ttusb_desc_interface_add_outp( usbd_config_t * pcfg, bulk_proc_cbk cbkf )
{
    int iret;

    iret = ttusb_desc_interface_add_endp( pcfg, USB_DIR_OUT, cbkf );
    if ( 0 != iret )  {
        return iret;
    }

    return 0;
}


static int usbd_get_device_status( uint8_t * ptr )
{
    uint16_t * pu16;

    // Device status bit mask
    // - Bit 0: Self Powered
    // - Bit 1: Remote Wakeup enabled
    pu16 = (uint16_t *)ptr;
    pu16[0] = 0x0;
    pu16[1] = 0x0;

    /**/
    return sizeof(uint16_t);
}


// return: descriptor length.
static int usbd_get_device_descriptor( usbd_config_t * pcfg, uint8_t * ptr )
{
    usb_device_descriptor_t * pdesc;

    /**/
    pdesc = (usb_device_descriptor_t *)ptr;
    pdesc->bLength = sizeof(usb_device_descriptor_t);
    pdesc->bDescriptorType = USB_DT_DEVICE;
    pdesc->bcdUSB = 0x0200;
    pdesc->bDeviceClass = 0;
    pdesc->bDeviceSubClass = 0;
    pdesc->bDeviceProtocol = 0;
    pdesc->bMaxPacketSize0 = 64;
    pdesc->idVendor = pcfg->vid;
    pdesc->idProduct = pcfg->pid;
    pdesc->bcdDevice = 0;
    pdesc->bNumConfigurations = 1;

    if ( pcfg->str_manu != NULL )  {
        pdesc->iManufacturer = 1;
    } else  {
        pdesc->iManufacturer = 0;
    }
    
    if ( pcfg->str_prod != NULL )  {
        pdesc->iProduct = 2;
    } else  {
        pdesc->iProduct = 0;
    }
    
    if ( pcfg->str_seri != NULL )  {
        pdesc->iSerialNumber = 3;
    } else  {
        pdesc->iSerialNumber = 0;
    }
    
    /**/
    return pdesc->bLength;
}


static int usbd_get_config_descriptor( usbd_config_t * pcfg, int rlen, uint8_t * ptr )
{
    int  i;
    usbd_intf_t * pifc = &( pcfg->iface[0] );
    usb_configuration_descriptor_t * pdesc_cfg;
    usb_interface_descriptor_t * pdesc_intf;
    usb_endpoint_descriptor_t * pdesc_endps;

    /**/
    pdesc_cfg = (usb_configuration_descriptor_t *)ptr;
    pdesc_cfg->bLength = sizeof( usb_configuration_descriptor_t );
    pdesc_cfg->bDescriptorType = USB_DT_CONFIG;
    pdesc_cfg->wTotalLength = sizeof( usb_configuration_descriptor_t );
    pdesc_cfg->wTotalLength = pdesc_cfg->wTotalLength + sizeof( usb_interface_descriptor_t );
    pdesc_cfg->wTotalLength = pdesc_cfg->wTotalLength + ( pifc->epcnt * sizeof(usb_endpoint_descriptor_t) );
    pdesc_cfg->bNumInterfaces = 1;
    pdesc_cfg->bConfigurationValue = 1;
    
    if ( pcfg->str_cnfg != NULL )  {
        pdesc_cfg->iConfiguration = 4;
    } else  {
        pdesc_cfg->iConfiguration = 0;    
    }
    
    pdesc_cfg->bmAttributes = 0x80;
    pdesc_cfg->bMaxPower = pcfg->maxpower;

    /**/
    if ( rlen > sizeof( usb_configuration_descriptor_t ) )
    {
        /* interfaces */
        pdesc_intf = (usb_interface_descriptor_t *)(pdesc_cfg + 1);
        pdesc_intf->bLength = sizeof(usb_interface_descriptor_t);
        pdesc_intf->bDescriptorType = USB_DT_INTERFACE;
        pdesc_intf->bInterfaceNumber = 0;
        pdesc_intf->bAlternateSetting = 0;
        pdesc_intf->bNumEndpoints = (uint8_t)pifc->epcnt;
        pdesc_intf->bInterfaceClass = 0xFF;
        pdesc_intf->bInterfaceSubClass = 0;
        pdesc_intf->bInterfaceProtocol = 0;
        
        if ( pifc->str_intf != NULL )  {
            pdesc_intf->iInterface = 5;
        } else  {
            pdesc_intf->iInterface = 0;
        }

        /* end points */
        pdesc_endps = (usb_endpoint_descriptor_t *)(pdesc_intf + 1);
        for ( i=0; i<pifc->epcnt; i++ )  {
            pdesc_endps[i].bLength = sizeof(usb_endpoint_descriptor_t);
            pdesc_endps[i].bDescriptorType = USB_DT_ENDPOINT;
            pdesc_endps[i].bEndpointAddress = pifc->epadrs[i];
            pdesc_endps[i].bmAttributes = USB_TRANSFER_TYPE_BULK;
            pdesc_endps[i].wMaxPacketSize = 512;
            pdesc_endps[i].bInterval = 0;
        }
    }

    /**/
    return pdesc_cfg->wTotalLength;
}


static int usbd_get_string_descriptor( usbd_config_t * pcfg, int idx, int lang, uint8_t * ptr )
{
    int  tofs;
    usbd_intf_t * pifc = &( pcfg->iface[0] );
    char * pstr;
    usb_string_descriptor_t * pdesc;

    /**/
    pdesc = (usb_string_descriptor_t *)ptr;
    pdesc->bDescriptorType = USB_DT_STRING;

    /**/
    switch (idx)  {
    case 0:
        /* Only support Language for english */
        pdesc->bLength = 4;
        pdesc->wArray[0] = 0x0409;
        return pdesc->bLength;    

    case 1:
        pstr = pcfg->str_manu;
        break;

    case 2:
        pstr = pcfg->str_prod;
        break;

    case 3:
        pstr = pcfg->str_seri;
        break;

    case 4:
        pstr = pcfg->str_cnfg;
        break;

    case 5:
        pstr = pifc->str_intf;
        break;
        
    case 0xEE:
        /* bVendorCode = 0x17 */
        pstr = "MSFT100\x17";
        break;

    default:
        pstr = NULL;
        break;
    }

    /* null string check */
    if ( pstr == NULL )  {
        return 0;
    }

    /* ascii to UNICODE(UTF-16LE) */
    tofs = 0;
    while ( *pstr != '\0' )  {
        pdesc->wArray[tofs] = *pstr;
        tofs += 1;
        pstr += 1;
    }

    pdesc->bLength = 2 + (uint8_t)(tofs << 1);

    /**/
    return pdesc->bLength;
}





static int usbd_setup_d2h_vendor( usb_setup_packet_t * pkt, uint8_t * ptr )
{
    int tsiz;
    uint8_t req = pkt->bRequest;
    usb_msos_compatid_descriptor_t * pcmp_desc;
    usb_msos_function_section_t * pfun_sec;
 
 
#if 0

    uint8_t rtype = pkt->bmRequestType;

    /*
    bmRequestType = 0xC0, wIndex = 04
    bmRequestType = 0xC1, wIndex = 05 
    */
    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        return 0;
    }
#endif


    /* MSFT100\x17"; */
    if ( req != 0x17 )  {
        return 0;
    }

    /**/
    switch ( pkt->wIndex )  {

    case 0x4:

        /**/
        tsiz = sizeof(usb_msos_compatid_descriptor_t) + sizeof(usb_msos_function_section_t);
        memset( ptr, 0, tsiz );

        /**/
        pcmp_desc = (usb_msos_compatid_descriptor_t *)ptr;
        pcmp_desc->dwLength = tsiz;
        pcmp_desc->bcdVersion = 0x0100;
        pcmp_desc->wIndex = 4;
        pcmp_desc->bCount = 1;
        
        /**/
        pfun_sec = (usb_msos_function_section_t *)(pcmp_desc + 1);
        memset( pfun_sec, 0, sizeof(usb_msos_function_section_t) );
        pfun_sec->bInterfaceNumber = 0;
        strcpy( (char *)pfun_sec->compatibleID, "WINUSB" );

        /**/
        break;
        
#if 0
    char * pstr;
    int tofs;
    usb_msos_ext_properties_descriptor_t * pext_desc;
    usb_msos_custom_property_section_t * pcus_sec;
    usb_msos_custom_property_data_t * pcus_dat;

 
    case 0x5:

        /* todo */
        pext_desc = (usb_msos_ext_properties_descriptor_t *)ptr;
        pext_desc->dwLength = sizeof(usb_msos_ext_properties_descriptor_t);
        pext_desc->bcdVersion = 0x0100;
        pext_desc->wIndex = 5;
        pext_desc->wCount = 1;

        /**/
        pcus_sec = (usb_msos_custom_property_section_t *)(pext_desc + 1);
        pcus_sec->dwSize = sizeof(usb_msos_custom_property_section_t);
        pcus_sec->dwPropertyDataType = 1;
        

        /**/
        tofs = 0;
        pstr = "DeviceInterfaceGUID";

        while ( *pstr != 0 )  {
            pcus_sec->wPropertyName[tofs] = *pstr;
            tofs += 1;
            pstr += 1;
        }

        pcus_sec->wPropertyName[tofs] = 0;
        tofs += 1;
        pcus_sec->dwPropertyNameLength = tofs * 2;


        /**/
        pcus_dat = (usb_msos_custom_property_data_t *)&pcus_sec->wPropertyName[tofs];
        tofs = 0;
        pstr = "{13341F84-F55B-3F0C-F579-F62D00EAEB05}";
        
        while ( *pstr != 0 )  {
            pcus_dat->wpropertyData[tofs] = *pstr;
            tofs += 1;
            pstr += 1;
        }

        pcus_dat->wpropertyData[tofs] = 0;
        tofs += 1;
        pcus_dat->dwPropertyDataLength = tofs * 2;

        /**/
        pcus_sec->dwSize = pcus_sec->dwSize + pcus_sec->dwPropertyNameLength + pcus_dat->dwPropertyDataLength;
        pext_desc->dwLength = pext_desc->dwLength + pcus_sec->dwSize;
        tsiz = pext_desc->dwLength;
        break;
#endif

    default:
        tsiz = 0;
        break;
    }

    /**/
    return tsiz;
}


/*
 可以返回数据给 host:
    这个函数的 return 值表示返回数据的字节长度.
    返回的数据内容就放在参数 ptr 指向的内存块中.
*/
int usbd_setup_d2h_proc( usbd_config_t * pcfg, usb_setup_packet_t * pkt, uint8_t * ptr )
{
    int  iret = 0;
    uint8_t rtype = pkt->bmRequestType;
    uint8_t req = pkt->bRequest;
    uint16_t dtype;

    if ( (rtype & USB_REQ_TYPE_TYPE_MASK) != USB_REQ_TYPE_TYPE_STANDARD )  {
        return usbd_setup_d2h_vendor( pkt, ptr );
    }

    if ( (rtype & USB_REQ_TYPE_RECIPIENT_MASK) != USB_REQ_TYPE_RECIPIENT_DEVICE )  {
        /* todo: ( INTERFACE, ENDPOINT ) */
        printf( "rtype, todo-1: %x\n", rtype );
        return 0;
    }

    switch ( req )  {
    case USB_REQUEST_GET_STATUS:
        iret = usbd_get_device_status( ptr );
        break;

    case USB_REQUEST_GET_DESCRIPTOR:
        dtype = pkt->wValue >> 8;

        switch ( dtype )  {
        case USB_DT_DEVICE:
            iret = usbd_get_device_descriptor( pcfg, ptr );
            break;

        case USB_DT_CONFIG:
            iret = usbd_get_config_descriptor( pcfg, pkt->wLength, ptr );
            break;

        case USB_DT_STRING:
            iret = usbd_get_string_descriptor( pcfg, pkt->wValue & 0xFF, pkt->wIndex, ptr );
            break;
        
        case USB_DT_BOS:
            /* todo : bos ... */
            break;

        case USB_DT_DEVICE_QUALIFIER:
            break;
            
        default:
            break;
        }

        break;

    case USB_REQUEST_GET_CONFIGURATION:
        break;

    default:
        printf( "todo:get, %u\n", req );
        break;
    }

    /**/
    return iret;
}


